/*-------------------------------------------------------------------------
 *
 * policy.c
 *	  操作政策的命令。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/backend/commands/policy.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup.h"
#include "access/htup_details.h"
#include "access/relation.h"
#include "access/sysattr.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_type.h"
#include "commands/policy.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/pg_list.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_node.h"
#include "parser/parse_relation.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rowsecurity.h"
#include "storage/lock.h"
#include "utils/acl.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"

static void fc_RangeVarCallbackForPolicy(const RangeVar *fc_rv,
									  Oid fc_relid, Oid fc_oldrelid, void *fc_arg);
static char fc_parse_policy_command(const char *fc_cmd_name);
static Datum *fc_policy_role_list_to_array(List *fc_roles, int *fc_num_roles);

/*
 * 回调到 RangeVarGetRelidExtended()。
 *
 * 检查以下内容：
 *	- 指定的关系是一个表。
 *	- 当前用户拥有该表。
 *	- 该表不是一个系统表。
 *
 * 如果其中任何检查失败，则会引发错误。
 */
static void fc_RangeVarCallbackForPolicy(const RangeVar *fc_rv, Oid fc_relid, Oid fc_oldrelid,
						  void *fc_arg)
{
	HeapTuple	fc_tuple;
	Form_pg_class fc_classform;
	char		fc_relkind;

	fc_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_relid));
	if (!HeapTupleIsValid(fc_tuple))
		return;

	fc_classform = (Form_pg_class) GETSTRUCT(fc_tuple);
	fc_relkind = fc_classform->relkind;

	/* 必须拥有关系。 */
	if (!pg_class_ownercheck(fc_relid, GetUserId()))
		aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(fc_relid)), fc_rv->relname);

	/* 除非明确允许，否则不得修改系统表。 */
	if (!allowSystemTableMods && IsSystemClass(fc_relid, fc_classform))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied: \"%s\" is a system catalog",
						fc_rv->relname)));

	/* 关系类型必须是一个表。 */
	if (fc_relkind != RELKIND_RELATION && fc_relkind != RELKIND_PARTITIONED_TABLE)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a table", fc_rv->relname)));

	ReleaseSysCache(fc_tuple);
}

/*
 * parse_policy_command -
 *	 将完整命令字符串转换为其字符表示的辅助函数。
 *
 * cmd_name - 完整的字符串命令名称。有效值为 'all'、'select'、
 *			  'insert'、'update' 和 'delete'。
 *
 */
static char fc_parse_policy_command(const char *fc_cmd_name)
{
	char		fc_polcmd;

	if (!fc_cmd_name)
		elog(ERROR, "unrecognized policy command");

	if (strcmp(fc_cmd_name, "all") == 0)
		fc_polcmd = '*';
	else if (strcmp(fc_cmd_name, "select") == 0)
		fc_polcmd = ACL_SELECT_CHR;
	else if (strcmp(fc_cmd_name, "insert") == 0)
		fc_polcmd = ACL_INSERT_CHR;
	else if (strcmp(fc_cmd_name, "update") == 0)
		fc_polcmd = ACL_UPDATE_CHR;
	else if (strcmp(fc_cmd_name, "delete") == 0)
		fc_polcmd = ACL_DELETE_CHR;
	else
		elog(ERROR, "unrecognized policy command");

	return fc_polcmd;
}

/*
 * policy_role_list_to_array
 *	 将 RoleSpecs 列表转换为角色 ID Datums 数组的辅助函数。
 */
static Datum *
fc_policy_role_list_to_array(List *fc_roles, int *fc_num_roles)
{
	Datum	   *fc_role_oids;
	ListCell   *fc_cell;
	int			fc_i = 0;

	/* 处理未传入角色视为适用于公共角色 */
	if (fc_roles == NIL)
	{
		*fc_num_roles = 1;
		fc_role_oids = (Datum *) palloc(*fc_num_roles * sizeof(Datum));
		fc_role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);

		return fc_role_oids;
	}

	*fc_num_roles = list_length(fc_roles);
	fc_role_oids = (Datum *) palloc(*fc_num_roles * sizeof(Datum));

	foreach(fc_cell, fc_roles)
	{
		RoleSpec   *fc_spec = lfirst(fc_cell);

		/*
		 * PUBLIC 包含所有角色，因此它单独存在才有意义。
		 */
		if (fc_spec->roletype == ROLESPEC_PUBLIC)
		{
			if (*fc_num_roles != 1)
			{
				ereport(WARNING,
						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
						 errmsg("ignoring specified roles other than PUBLIC"),
						 errhint("All roles are members of the PUBLIC role.")));
				*fc_num_roles = 1;
			}
			fc_role_oids[0] = ObjectIdGetDatum(ACL_ID_PUBLIC);

			return fc_role_oids;
		}
		else
			fc_role_oids[fc_i++] =
				ObjectIdGetDatum(get_rolespec_oid(fc_spec, false));
	}

	return fc_role_oids;
}

/*
 * 从目录加载行安全策略，并将其存储在
 * 关系的 relcache 条目中。
 *
 * 请注意，调用者应已验证 pg_class.relrowsecurity
 * 对于该关系为 true。
 */
void RelationBuildRowSecurity(Relation fc_relation)
{
	MemoryContext fc_rscxt;
	MemoryContext fc_oldcxt = CurrentMemoryContext;
	RowSecurityDesc *fc_rsdesc;
	Relation	fc_catalog;
	ScanKeyData fc_skey;
	SysScanDesc fc_sscan;
	HeapTuple	fc_tuple;

	/*
	 * 创建一个内存上下文，以保存与该
	 * 关系的行安全策略相关的所有内容。这使得在
	 * relcache 刷新时清理变得容易。然而，为了涵盖
	 * 在过程中发生错误的可能性，我们不会让该上下文在完成之前
	 * 存活较长时间。
	 */
	fc_rscxt = AllocSetContextCreate(CurrentMemoryContext,
								  "row security descriptor",
								  ALLOCSET_SMALL_SIZES);
	MemoryContextCopyAndSetIdentifier(fc_rscxt,
									  RelationGetRelationName(fc_relation));

	fc_rsdesc = MemoryContextAllocZero(fc_rscxt, sizeof(RowSecurityDesc));
	fc_rsdesc->rscxt = fc_rscxt;

	/*
	 * 现在扫描 pg_policy，以查找与该关系相关的 RLS 策略。
	 * 因为我们使用 (polrelid, polname) 的索引，所以我们应该
	 * 在名称顺序中始终访问关系的策略，至少在系统索引
	 * 没有被禁用时。这简化了 equalRSDesc()。
	 */
	fc_catalog = table_open(PolicyRelationId, AccessShareLock);

	ScanKeyInit(&fc_skey,
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(fc_relation)));

	fc_sscan = systable_beginscan(fc_catalog, PolicyPolrelidPolnameIndexId, true,
							   NULL, 1, &fc_skey);

	while (HeapTupleIsValid(fc_tuple = systable_getnext(fc_sscan)))
	{
		Form_pg_policy fc_policy_form = (Form_pg_policy) GETSTRUCT(fc_tuple);
		RowSecurityPolicy *fc_policy;
		Datum		fc_datum;
		bool		fc_isnull;
		char	   *fc_str_value;

		fc_policy = MemoryContextAllocZero(fc_rscxt, sizeof(RowSecurityPolicy));

		/*
		 * 注意：我们必须确保按引用传递的数据被复制到
		 * rscxt。然而，我们尽量不让该上下文在比
		 * 我们需要的更宽的范围内处于当前状态。
		 */

		/* 获取策略命令 */
		fc_policy->polcmd = fc_policy_form->polcmd;

		/* 获取策略，宽松或限制性 */
		fc_policy->permissive = fc_policy_form->polpermissive;

		/* 获取策略名称 */
		fc_policy->policy_name =
			MemoryContextStrdup(fc_rscxt, NameStr(fc_policy_form->polname));

		/* 获取策略角色 */
		fc_datum = heap_getattr(fc_tuple, Anum_pg_policy_polroles,
							 RelationGetDescr(fc_catalog), &fc_isnull);
		/* 不应该为 null，但我们还是检查一下以祈求好运 */
		if (fc_isnull)
			elog(ERROR, "unexpected null value in pg_policy.polroles");
		MemoryContextSwitchTo(fc_rscxt);
		fc_policy->roles = DatumGetArrayTypePCopy(fc_datum);
		MemoryContextSwitchTo(fc_oldcxt);

		/* 获取策略条件 */
		fc_datum = heap_getattr(fc_tuple, Anum_pg_policy_polqual,
							 RelationGetDescr(fc_catalog), &fc_isnull);
		if (!fc_isnull)
		{
			fc_str_value = TextDatumGetCString(fc_datum);
			MemoryContextSwitchTo(fc_rscxt);
			fc_policy->qual = (Expr *) stringToNode(fc_str_value);
			MemoryContextSwitchTo(fc_oldcxt);
			pfree(fc_str_value);
		}
		else
			fc_policy->qual = NULL;

		/* 获取 WITH CHECK 条件 */
		fc_datum = heap_getattr(fc_tuple, Anum_pg_policy_polwithcheck,
							 RelationGetDescr(fc_catalog), &fc_isnull);
		if (!fc_isnull)
		{
			fc_str_value = TextDatumGetCString(fc_datum);
			MemoryContextSwitchTo(fc_rscxt);
			fc_policy->with_check_qual = (Expr *) stringToNode(fc_str_value);
			MemoryContextSwitchTo(fc_oldcxt);
			pfree(fc_str_value);
		}
		else
			fc_policy->with_check_qual = NULL;

		/* 我们想缓存这些表达式中是否有子链接 */
		fc_policy->hassublinks = checkExprHasSubLink((Node *) fc_policy->qual) ||
			checkExprHasSubLink((Node *) fc_policy->with_check_qual);

		/*
		 * 将此对象添加到列表。出于历史原因，列表是以
		 * 反向顺序构建的。
		 */
		MemoryContextSwitchTo(fc_rscxt);
		fc_rsdesc->policies = lcons(fc_policy, fc_rsdesc->policies);
		MemoryContextSwitchTo(fc_oldcxt);
	}

	systable_endscan(fc_sscan);
	table_close(fc_catalog, AccessShareLock);

	/*
	 * 成功。将描述符的内存上下文重新附加到
	 * CacheMemoryContext，以便它将无限期存在，
	 * 然后将策略描述符附加到 relcache 条目。
	 */
	MemoryContextSetParent(fc_rscxt, CacheMemoryContext);

	fc_relation->rd_rsdesc = fc_rsdesc;
}

/*
 * RemovePolicyById -
 *	 按其 OID 删除策略。如果指定的 oid 不存在
 *	 策略，则会引发错误。
 *
 * policy_id - 策略的 oid。
 */
void RemovePolicyById(Oid fc_policy_id)
{
	Relation	fc_pg_policy_rel;
	SysScanDesc fc_sscan;
	ScanKeyData fc_skey[1];
	HeapTuple	fc_tuple;
	Oid			fc_relid;
	Relation	fc_rel;

	fc_pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);

	/*
	 * 查找要删除的策略。
	 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_policy_id));

	fc_sscan = systable_beginscan(fc_pg_policy_rel, PolicyOidIndexId, true,
							   NULL, 1, fc_skey);

	fc_tuple = systable_getnext(fc_sscan);

	/* 如果政策存在，则删除它；否则引发错误。 */
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "could not find tuple for policy %u", fc_policy_id);

	/*
	 * 打开并对政策所属的关系进行独占锁定。 （我们需要独占锁定以锁定可能依赖于关系的政策集的查询；此外，我们必须持有锁直到提交。）
	 */
	fc_relid = ((Form_pg_policy) GETSTRUCT(fc_tuple))->polrelid;

	fc_rel = table_open(fc_relid, AccessExclusiveLock);
	if (fc_rel->rd_rel->relkind != RELKIND_RELATION &&
		fc_rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
		ereport(ERROR,
				(errcode(ERRCODE_WRONG_OBJECT_TYPE),
				 errmsg("\"%s\" is not a table",
						RelationGetRelationName(fc_rel))));

	if (!allowSystemTableMods && IsSystemRelation(fc_rel))
		ereport(ERROR,
				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
				 errmsg("permission denied: \"%s\" is a system catalog",
						RelationGetRelationName(fc_rel))));

	CatalogTupleDelete(fc_pg_policy_rel, &fc_tuple->t_self);

	systable_endscan(fc_sscan);

	/*
	 * 请注意，与 pg_class 中的一些其他标志不同，relrowsecurity 不仅仅是指示是否存在政策。 当用户设置 relrowsecurity 时，所有对该关系的访问必须通过政策进行。 如果该关系未定义任何政策，则会创建一个默认拒绝政策，所有记录都会被过滤（除了来自所有者的查询）。
	 */
	CacheInvalidateRelcache(fc_rel);

	table_close(fc_rel, NoLock);

	/* 清理 */
	table_close(fc_pg_policy_rel, RowExclusiveLock);
}

/*
 * RemoveRoleFromObjectPolicy -
 *	 从政策的适用角色列表中移除一个角色。
 *
 * 如果角色成功从政策中移除则返回 true。 
 * 如果角色未被移除，因为这将使 polroles 为空（这是不允许的，尽管可能不应该如此），则返回 false。
 * 在返回 false 的情况下，调用者应当彻底删除该政策。
 *
 * roleid - 要移除的角色的 oid
 * classid - 应始终为 PolicyRelationId
 * policy_id - 政策的 oid。
 */
bool RemoveRoleFromObjectPolicy(Oid fc_roleid, Oid fc_classid, Oid fc_policy_id)
{
	Relation	fc_pg_policy_rel;
	SysScanDesc fc_sscan;
	ScanKeyData fc_skey[1];
	HeapTuple	fc_tuple;
	Oid			fc_relid;
	ArrayType  *fc_policy_roles;
	Datum		fc_roles_datum;
	Oid		   *fc_roles;
	int			fc_num_roles;
	Datum	   *fc_role_oids;
	bool		fc_attr_isnull;
	bool		fc_keep_policy = true;
	int			fc_i,
				fc_j;

	Assert(fc_classid == PolicyRelationId);

	fc_pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);

	/*
	 * 查找要更新的政策。
	 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_oid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_policy_id));

	fc_sscan = systable_beginscan(fc_pg_policy_rel, PolicyOidIndexId, true,
							   NULL, 1, fc_skey);

	fc_tuple = systable_getnext(fc_sscan);

	/* 如果没有找到政策则引发错误。 */
	if (!HeapTupleIsValid(fc_tuple))
		elog(ERROR, "could not find tuple for policy %u", fc_policy_id);

	/* 确定政策所属的关系 */
	fc_relid = ((Form_pg_policy) GETSTRUCT(fc_tuple))->polrelid;

	/* 获取当前角色集 */
	fc_roles_datum = heap_getattr(fc_tuple,
							   Anum_pg_policy_polroles,
							   RelationGetDescr(fc_pg_policy_rel),
							   &fc_attr_isnull);

	Assert(!fc_attr_isnull);

	fc_policy_roles = DatumGetArrayTypePCopy(fc_roles_datum);
	fc_roles = (Oid *) ARR_DATA_PTR(fc_policy_roles);
	fc_num_roles = ARR_DIMS(fc_policy_roles)[0];

	/*
	 * 重新构建 polroles 数组，不包含目标角色的任何提及。
	 * 通常情况下，应该恰好有一个，但我们必须处理重复的提及，因为 CREATE/ALTER POLICY 历史上允许这样。
	 */
	fc_role_oids = (Datum *) palloc(fc_num_roles * sizeof(Datum));
	for (fc_i = 0, fc_j = 0; fc_i < fc_num_roles; fc_i++)
	{
		if (fc_roles[fc_i] != fc_roleid)
			fc_role_oids[fc_j++] = ObjectIdGetDatum(fc_roles[fc_i]);
	}
	fc_num_roles = fc_j;

	/* 如果还有任何角色，则更新政策条目。 */
	if (fc_num_roles > 0)
	{
		ArrayType  *fc_role_ids;
		Datum		fc_values[Natts_pg_policy];
		bool		fc_isnull[Natts_pg_policy];
		bool		fc_replaces[Natts_pg_policy];
		HeapTuple	fc_new_tuple;
		HeapTuple	fc_reltup;
		ObjectAddress fc_target;
		ObjectAddress fc_myself;

		/* 清零 */
		memset(fc_values, 0, sizeof(fc_values));
		memset(fc_replaces, 0, sizeof(fc_replaces));
		memset(fc_isnull, 0, sizeof(fc_isnull));

		/* 这是新元组的数组 */
		fc_role_ids = construct_array(fc_role_oids, fc_num_roles, OIDOID,
								   sizeof(Oid), true, TYPALIGN_INT);

		fc_replaces[Anum_pg_policy_polroles - 1] = true;
		fc_values[Anum_pg_policy_polroles - 1] = PointerGetDatum(fc_role_ids);

		fc_new_tuple = heap_modify_tuple(fc_tuple,
									  RelationGetDescr(fc_pg_policy_rel),
									  fc_values, fc_isnull, fc_replaces);
		CatalogTupleUpdate(fc_pg_policy_rel, &fc_new_tuple->t_self, fc_new_tuple);

		/* 移除所有旧的共享依赖项（角色） */
		deleteSharedDependencyRecordsFor(PolicyRelationId, fc_policy_id, 0);

		/* 记录新的共享依赖项（角色） */
		fc_myself.classId = PolicyRelationId;
		fc_myself.objectId = fc_policy_id;
		fc_myself.objectSubId = 0;

		fc_target.classId = AuthIdRelationId;
		fc_target.objectSubId = 0;
		for (fc_i = 0; fc_i < fc_num_roles; fc_i++)
		{
			fc_target.objectId = DatumGetObjectId(fc_role_oids[fc_i]);
			/* 不需要对公共角色的依赖 */
			if (fc_target.objectId != ACL_ID_PUBLIC)
				recordSharedDependencyOn(&fc_myself, &fc_target,
										 SHARED_DEPENDENCY_POLICY);
		}

		InvokeObjectPostAlterHook(PolicyRelationId, fc_policy_id, 0);

		heap_freetuple(fc_new_tuple);

		/* 使更新可见 */
		CommandCounterIncrement();

		/*
		 * 使政策所属关系的 relcache 条目失效，以强制重新执行任何依赖计划。 如果出现竞争条件，关系刚被删除，我们则不需要做任何事情。
		 */
		fc_reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(fc_relid));
		if (HeapTupleIsValid(fc_reltup))
		{
			CacheInvalidateRelcacheByTuple(fc_reltup);
			ReleaseSysCache(fc_reltup);
		}
	}
	else
	{
		/* 不会剩下任何角色，因此删除该政策。 */
		fc_keep_policy = false;
	}

	/* 清理。 */
	systable_endscan(fc_sscan);

	table_close(fc_pg_policy_rel, RowExclusiveLock);

	return fc_keep_policy;
}

/*
 * CreatePolicy -
 *	 处理 CREATE POLICY 命令的执行。
 *
 * stmt - 描述要创建的政策的 CreatePolicyStmt。
 */
ObjectAddress CreatePolicy(CreatePolicyStmt *fc_stmt)
{
	Relation	fc_pg_policy_rel;
	Oid			fc_policy_id;
	Relation	fc_target_table;
	Oid			fc_table_id;
	char		fc_polcmd;
	Datum	   *fc_role_oids;
	int			fc_nitems = 0;
	ArrayType  *fc_role_ids;
	ParseState *fc_qual_pstate;
	ParseState *fc_with_check_pstate;
	ParseNamespaceItem *fc_nsitem;
	Node	   *fc_qual;
	Node	   *fc_with_check_qual;
	ScanKeyData fc_skey[2];
	SysScanDesc fc_sscan;
	HeapTuple	fc_policy_tuple;
	Datum		fc_values[Natts_pg_policy];
	bool		fc_isnull[Natts_pg_policy];
	ObjectAddress fc_target;
	ObjectAddress fc_myself;
	int			fc_i;

	/* 解析命令 */
	fc_polcmd = fc_parse_policy_command(fc_stmt->cmd_name);

	/*
	 * 如果命令是 SELECT 或 DELETE，则 WITH CHECK 应为 NULL。
	 */
	if ((fc_polcmd == ACL_SELECT_CHR || fc_polcmd == ACL_DELETE_CHR)
		&& fc_stmt->with_check != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("WITH CHECK cannot be applied to SELECT or DELETE")));

	/*
	 * 如果命令是 INSERT，则 WITH CHECK 应是唯一提供的表达式。
	 */
	if (fc_polcmd == ACL_INSERT_CHR && fc_stmt->qual != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("only WITH CHECK expression allowed for INSERT")));

	/* 收集角色 id */
	fc_role_oids = fc_policy_role_list_to_array(fc_stmt->roles, &fc_nitems);
	fc_role_ids = construct_array(fc_role_oids, fc_nitems, OIDOID,
							   sizeof(Oid), true, TYPALIGN_INT);

	/* 解析提供的子句 */
	fc_qual_pstate = make_parsestate(NULL);
	fc_with_check_pstate = make_parsestate(NULL);

	/* 清零 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_isnull, 0, sizeof(fc_isnull));

	/* 获取表的 id。同时处理权限检查。 */
	fc_table_id = RangeVarGetRelidExtended(fc_stmt->table, AccessExclusiveLock,
										0,
										fc_RangeVarCallbackForPolicy,
										(void *) fc_stmt);

	/* 打开目标表以构建条件。不需要额外的锁。 */
	fc_target_table = relation_open(fc_table_id, NoLock);

	/* 添加常规安全条件 */
	fc_nsitem = addRangeTableEntryForRelation(fc_qual_pstate, fc_target_table,
										   AccessShareLock,
										   NULL, false, false);
	addNSItemToQuery(fc_qual_pstate, fc_nsitem, false, true, true);

	/* 添加核实条件 */
	fc_nsitem = addRangeTableEntryForRelation(fc_with_check_pstate, fc_target_table,
										   AccessShareLock,
										   NULL, false, false);
	addNSItemToQuery(fc_with_check_pstate, fc_nsitem, false, true, true);

	fc_qual = transformWhereClause(fc_qual_pstate,
								fc_stmt->qual,
								EXPR_KIND_POLICY,
								"POLICY");

	fc_with_check_qual = transformWhereClause(fc_with_check_pstate,
										   fc_stmt->with_check,
										   EXPR_KIND_POLICY,
										   "POLICY");

	/* 修复排序信息 */
	assign_expr_collations(fc_qual_pstate, fc_qual);
	assign_expr_collations(fc_with_check_pstate, fc_with_check_qual);

	/* 打开 pg_policy 目录 */
	fc_pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);

	/* 设置关键 - 策略的关系 ID。 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_table_id));

	/* 设置关键 - 策略的名称。 */
	ScanKeyInit(&fc_skey[1],
				Anum_pg_policy_polname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_stmt->policy_name));

	fc_sscan = systable_beginscan(fc_pg_policy_rel,
							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
							   fc_skey);

	fc_policy_tuple = systable_getnext(fc_sscan);

	/* 如果表中已存在该策略名称则抱怨 */
	if (HeapTupleIsValid(fc_policy_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("policy \"%s\" for table \"%s\" already exists",
						fc_stmt->policy_name, RelationGetRelationName(fc_target_table))));

	fc_policy_id = GetNewOidWithIndex(fc_pg_policy_rel, PolicyOidIndexId,
								   Anum_pg_policy_oid);
	fc_values[Anum_pg_policy_oid - 1] = ObjectIdGetDatum(fc_policy_id);
	fc_values[Anum_pg_policy_polrelid - 1] = ObjectIdGetDatum(fc_table_id);
	fc_values[Anum_pg_policy_polname - 1] = DirectFunctionCall1(namein,
															 CStringGetDatum(fc_stmt->policy_name));
	fc_values[Anum_pg_policy_polcmd - 1] = CharGetDatum(fc_polcmd);
	fc_values[Anum_pg_policy_polpermissive - 1] = BoolGetDatum(fc_stmt->permissive);
	fc_values[Anum_pg_policy_polroles - 1] = PointerGetDatum(fc_role_ids);

	/* 如果存在则添加 qual。 */
	if (fc_qual)
		fc_values[Anum_pg_policy_polqual - 1] = CStringGetTextDatum(nodeToString(fc_qual));
	else
		fc_isnull[Anum_pg_policy_polqual - 1] = true;

	/* 如果存在则添加 WITH CHECK qual */
	if (fc_with_check_qual)
		fc_values[Anum_pg_policy_polwithcheck - 1] = CStringGetTextDatum(nodeToString(fc_with_check_qual));
	else
		fc_isnull[Anum_pg_policy_polwithcheck - 1] = true;

	fc_policy_tuple = heap_form_tuple(RelationGetDescr(fc_pg_policy_rel), fc_values,
								   fc_isnull);

	CatalogTupleInsert(fc_pg_policy_rel, fc_policy_tuple);

	/* 记录依赖关系 */
	fc_target.classId = RelationRelationId;
	fc_target.objectId = fc_table_id;
	fc_target.objectSubId = 0;

	fc_myself.classId = PolicyRelationId;
	fc_myself.objectId = fc_policy_id;
	fc_myself.objectSubId = 0;

	recordDependencyOn(&fc_myself, &fc_target, DEPENDENCY_AUTO);

	recordDependencyOnExpr(&fc_myself, fc_qual, fc_qual_pstate->p_rtable,
						   DEPENDENCY_NORMAL);

	recordDependencyOnExpr(&fc_myself, fc_with_check_qual,
						   fc_with_check_pstate->p_rtable, DEPENDENCY_NORMAL);

	/* 注册角色依赖关系 */
	fc_target.classId = AuthIdRelationId;
	fc_target.objectSubId = 0;
	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		fc_target.objectId = DatumGetObjectId(fc_role_oids[fc_i]);
		/* 如果是公共的则没有依赖关系 */
		if (fc_target.objectId != ACL_ID_PUBLIC)
			recordSharedDependencyOn(&fc_myself, &fc_target,
									 SHARED_DEPENDENCY_POLICY);
	}

	InvokeObjectPostCreateHook(PolicyRelationId, fc_policy_id, 0);

	/* 使关系缓存无效 */
	CacheInvalidateRelcache(fc_target_table);

	/* 清理。 */
	heap_freetuple(fc_policy_tuple);
	free_parsestate(fc_qual_pstate);
	free_parsestate(fc_with_check_pstate);
	systable_endscan(fc_sscan);
	relation_close(fc_target_table, NoLock);
	table_close(fc_pg_policy_rel, RowExclusiveLock);

	return fc_myself;
}

/*
 * AlterPolicy -
 *	 处理 ALTER POLICY 命令的执行。
 *
 * stmt - 描述策略及如何修改它的 AlterPolicyStmt。
 */
ObjectAddress AlterPolicy(AlterPolicyStmt *fc_stmt)
{
	Relation	fc_pg_policy_rel;
	Oid			fc_policy_id;
	Relation	fc_target_table;
	Oid			fc_table_id;
	Datum	   *fc_role_oids = NULL;
	int			fc_nitems = 0;
	ArrayType  *fc_role_ids = NULL;
	List	   *fc_qual_parse_rtable = NIL;
	List	   *fc_with_check_parse_rtable = NIL;
	Node	   *fc_qual = NULL;
	Node	   *fc_with_check_qual = NULL;
	ScanKeyData fc_skey[2];
	SysScanDesc fc_sscan;
	HeapTuple	fc_policy_tuple;
	HeapTuple	fc_new_tuple;
	Datum		fc_values[Natts_pg_policy];
	bool		fc_isnull[Natts_pg_policy];
	bool		fc_replaces[Natts_pg_policy];
	ObjectAddress fc_target;
	ObjectAddress fc_myself;
	Datum		fc_polcmd_datum;
	char		fc_polcmd;
	bool		fc_polcmd_isnull;
	int			fc_i;

	/* 解析 role_ids */
	if (fc_stmt->roles != NULL)
	{
		fc_role_oids = fc_policy_role_list_to_array(fc_stmt->roles, &fc_nitems);
		fc_role_ids = construct_array(fc_role_oids, fc_nitems, OIDOID,
								   sizeof(Oid), true, TYPALIGN_INT);
	}

	/* 获取表的 id。同时处理权限检查。 */
	fc_table_id = RangeVarGetRelidExtended(fc_stmt->table, AccessExclusiveLock,
										0,
										fc_RangeVarCallbackForPolicy,
										(void *) fc_stmt);

	fc_target_table = relation_open(fc_table_id, NoLock);

	/* 解析 using 策略子句 */
	if (fc_stmt->qual)
	{
		ParseNamespaceItem *fc_nsitem;
		ParseState *fc_qual_pstate = make_parsestate(NULL);

		fc_nsitem = addRangeTableEntryForRelation(fc_qual_pstate, fc_target_table,
											   AccessShareLock,
											   NULL, false, false);

		addNSItemToQuery(fc_qual_pstate, fc_nsitem, false, true, true);

		fc_qual = transformWhereClause(fc_qual_pstate, fc_stmt->qual,
									EXPR_KIND_POLICY,
									"POLICY");

		/* 修复排序信息 */
		assign_expr_collations(fc_qual_pstate, fc_qual);

		fc_qual_parse_rtable = fc_qual_pstate->p_rtable;
		free_parsestate(fc_qual_pstate);
	}

	/* 解析 with-check 策略子句 */
	if (fc_stmt->with_check)
	{
		ParseNamespaceItem *fc_nsitem;
		ParseState *fc_with_check_pstate = make_parsestate(NULL);

		fc_nsitem = addRangeTableEntryForRelation(fc_with_check_pstate, fc_target_table,
											   AccessShareLock,
											   NULL, false, false);

		addNSItemToQuery(fc_with_check_pstate, fc_nsitem, false, true, true);

		fc_with_check_qual = transformWhereClause(fc_with_check_pstate,
											   fc_stmt->with_check,
											   EXPR_KIND_POLICY,
											   "POLICY");

		/* 修复排序信息 */
		assign_expr_collations(fc_with_check_pstate, fc_with_check_qual);

		fc_with_check_parse_rtable = fc_with_check_pstate->p_rtable;
		free_parsestate(fc_with_check_pstate);
	}

	/* 清零 */
	memset(fc_values, 0, sizeof(fc_values));
	memset(fc_replaces, 0, sizeof(fc_replaces));
	memset(fc_isnull, 0, sizeof(fc_isnull));

	/* 找到要更新的策略。 */
	fc_pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);

	/* 设置关键 - 策略的关系 ID。 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_table_id));

	/* 设置关键 - 策略的名称。 */
	ScanKeyInit(&fc_skey[1],
				Anum_pg_policy_polname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_stmt->policy_name));

	fc_sscan = systable_beginscan(fc_pg_policy_rel,
							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
							   fc_skey);

	fc_policy_tuple = systable_getnext(fc_sscan);

	/* 检查是否找到策略，如果未找到则引发错误。 */
	if (!HeapTupleIsValid(fc_policy_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("policy \"%s\" for table \"%s\" does not exist",
						fc_stmt->policy_name,
						RelationGetRelationName(fc_target_table))));

	/* 获取策略命令 */
	fc_polcmd_datum = heap_getattr(fc_policy_tuple, Anum_pg_policy_polcmd,
								RelationGetDescr(fc_pg_policy_rel),
								&fc_polcmd_isnull);
	Assert(!fc_polcmd_isnull);
	fc_polcmd = DatumGetChar(fc_polcmd_datum);

	/*
	 * 如果命令是 SELECT 或 DELETE，则 WITH CHECK 应为 NULL。
	 */
	if ((fc_polcmd == ACL_SELECT_CHR || fc_polcmd == ACL_DELETE_CHR)
		&& fc_stmt->with_check != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("only USING expression allowed for SELECT, DELETE")));

	/*
	 * 如果命令是 INSERT，则 WITH CHECK 应是唯一提供的表达式。
	 */
	if ((fc_polcmd == ACL_INSERT_CHR)
		&& fc_stmt->qual != NULL)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("only WITH CHECK expression allowed for INSERT")));

	fc_policy_id = ((Form_pg_policy) GETSTRUCT(fc_policy_tuple))->oid;

	if (fc_role_ids != NULL)
	{
		fc_replaces[Anum_pg_policy_polroles - 1] = true;
		fc_values[Anum_pg_policy_polroles - 1] = PointerGetDatum(fc_role_ids);
	}
	else
	{
		Oid		   *fc_roles;
		Datum		fc_roles_datum;
		bool		fc_attr_isnull;
		ArrayType  *fc_policy_roles;

		/*
		 * 我们需要从目录中提取该策略适用的角色集合，
		 * 以便能够正确地重建策略的依赖关系。
		 */

		fc_roles_datum = heap_getattr(fc_policy_tuple, Anum_pg_policy_polroles,
								   RelationGetDescr(fc_pg_policy_rel),
								   &fc_attr_isnull);
		Assert(!fc_attr_isnull);

		fc_policy_roles = DatumGetArrayTypePCopy(fc_roles_datum);

		fc_roles = (Oid *) ARR_DATA_PTR(fc_policy_roles);

		fc_nitems = ARR_DIMS(fc_policy_roles)[0];

		fc_role_oids = (Datum *) palloc(fc_nitems * sizeof(Datum));

		for (fc_i = 0; fc_i < fc_nitems; fc_i++)
			fc_role_oids[fc_i] = ObjectIdGetDatum(fc_roles[fc_i]);
	}

	if (fc_qual != NULL)
	{
		fc_replaces[Anum_pg_policy_polqual - 1] = true;
		fc_values[Anum_pg_policy_polqual - 1]
			= CStringGetTextDatum(nodeToString(fc_qual));
	}
	else
	{
		Datum		fc_value_datum;
		bool		fc_attr_isnull;

		/*
		 * 我们需要提取 USING 表达式并从目录中为
		 * 策略构建范围表，以便能够正确地重建
		 * 策略的依赖关系。
		 */

		/* 检查策略是否有 USING 表达式 */
		fc_value_datum = heap_getattr(fc_policy_tuple, Anum_pg_policy_polqual,
								   RelationGetDescr(fc_pg_policy_rel),
								   &fc_attr_isnull);
		if (!fc_attr_isnull)
		{
			char	   *fc_qual_value;
			ParseState *fc_qual_pstate;

			/* parsestate 仅用于构建范围表 */
			fc_qual_pstate = make_parsestate(NULL);

			fc_qual_value = TextDatumGetCString(fc_value_datum);
			fc_qual = stringToNode(fc_qual_value);

			/* 将该关系添加到 parsestate 的 rangetable，以便处理依赖关系 */
			(void) addRangeTableEntryForRelation(fc_qual_pstate, fc_target_table,
												 AccessShareLock,
												 NULL, false, false);

			fc_qual_parse_rtable = fc_qual_pstate->p_rtable;
			free_parsestate(fc_qual_pstate);
		}
	}

	if (fc_with_check_qual != NULL)
	{
		fc_replaces[Anum_pg_policy_polwithcheck - 1] = true;
		fc_values[Anum_pg_policy_polwithcheck - 1]
			= CStringGetTextDatum(nodeToString(fc_with_check_qual));
	}
	else
	{
		Datum		fc_value_datum;
		bool		fc_attr_isnull;

		/*
		 * 我们需要提取 WITH CHECK 表达式并从目录中为
		 * 策略构建范围表，以便能够正确地重建
		 * 策略的依赖关系。
		 */

		/* 检查策略是否有 WITH CHECK 表达式 */
		fc_value_datum = heap_getattr(fc_policy_tuple, Anum_pg_policy_polwithcheck,
								   RelationGetDescr(fc_pg_policy_rel),
								   &fc_attr_isnull);
		if (!fc_attr_isnull)
		{
			char	   *fc_with_check_value;
			ParseState *fc_with_check_pstate;

			/* parsestate 仅用于构建范围表 */
			fc_with_check_pstate = make_parsestate(NULL);

			fc_with_check_value = TextDatumGetCString(fc_value_datum);
			fc_with_check_qual = stringToNode(fc_with_check_value);

			/* 将该关系添加到 parsestate 的 rangetable，以便处理依赖关系 */
			(void) addRangeTableEntryForRelation(fc_with_check_pstate,
												 fc_target_table,
												 AccessShareLock,
												 NULL, false, false);

			fc_with_check_parse_rtable = fc_with_check_pstate->p_rtable;
			free_parsestate(fc_with_check_pstate);
		}
	}

	fc_new_tuple = heap_modify_tuple(fc_policy_tuple,
								  RelationGetDescr(fc_pg_policy_rel),
								  fc_values, fc_isnull, fc_replaces);
	CatalogTupleUpdate(fc_pg_policy_rel, &fc_new_tuple->t_self, fc_new_tuple);

	/* 更新依赖关系。 */
	deleteDependencyRecordsFor(PolicyRelationId, fc_policy_id, false);

	/* 记录依赖关系 */
	fc_target.classId = RelationRelationId;
	fc_target.objectId = fc_table_id;
	fc_target.objectSubId = 0;

	fc_myself.classId = PolicyRelationId;
	fc_myself.objectId = fc_policy_id;
	fc_myself.objectSubId = 0;

	recordDependencyOn(&fc_myself, &fc_target, DEPENDENCY_AUTO);

	recordDependencyOnExpr(&fc_myself, fc_qual, fc_qual_parse_rtable, DEPENDENCY_NORMAL);

	recordDependencyOnExpr(&fc_myself, fc_with_check_qual, fc_with_check_parse_rtable,
						   DEPENDENCY_NORMAL);

	/* 注册角色依赖关系 */
	deleteSharedDependencyRecordsFor(PolicyRelationId, fc_policy_id, 0);
	fc_target.classId = AuthIdRelationId;
	fc_target.objectSubId = 0;
	for (fc_i = 0; fc_i < fc_nitems; fc_i++)
	{
		fc_target.objectId = DatumGetObjectId(fc_role_oids[fc_i]);
		/* 如果是公共的则没有依赖关系 */
		if (fc_target.objectId != ACL_ID_PUBLIC)
			recordSharedDependencyOn(&fc_myself, &fc_target,
									 SHARED_DEPENDENCY_POLICY);
	}

	InvokeObjectPostAlterHook(PolicyRelationId, fc_policy_id, 0);

	heap_freetuple(fc_new_tuple);

	/* 使关系缓存无效 */
	CacheInvalidateRelcache(fc_target_table);

	/* 清理。 */
	systable_endscan(fc_sscan);
	relation_close(fc_target_table, NoLock);
	table_close(fc_pg_policy_rel, RowExclusiveLock);

	return fc_myself;
}

/*
 * rename_policy -
 *	 更改关系上策略的名称
 */
ObjectAddress rename_policy(RenameStmt *fc_stmt)
{
	Relation	fc_pg_policy_rel;
	Relation	fc_target_table;
	Oid			fc_table_id;
	Oid			fc_opoloid;
	ScanKeyData fc_skey[2];
	SysScanDesc fc_sscan;
	HeapTuple	fc_policy_tuple;
	ObjectAddress fc_address;

	/* 获取表的 id。同时处理权限检查。 */
	fc_table_id = RangeVarGetRelidExtended(fc_stmt->relation, AccessExclusiveLock,
										0,
										fc_RangeVarCallbackForPolicy,
										(void *) fc_stmt);

	fc_target_table = relation_open(fc_table_id, NoLock);

	fc_pg_policy_rel = table_open(PolicyRelationId, RowExclusiveLock);

	/* 第一遍 -- 检查冲突 */

	/* 添加关键 - 策略的关系 ID。 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_table_id));

	/* 添加关键 - 策略的名称。 */
	ScanKeyInit(&fc_skey[1],
				Anum_pg_policy_polname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_stmt->newname));

	fc_sscan = systable_beginscan(fc_pg_policy_rel,
							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
							   fc_skey);

	if (HeapTupleIsValid(systable_getnext(fc_sscan)))
		ereport(ERROR,
				(errcode(ERRCODE_DUPLICATE_OBJECT),
				 errmsg("policy \"%s\" for table \"%s\" already exists",
						fc_stmt->newname, RelationGetRelationName(fc_target_table))));

	systable_endscan(fc_sscan);

	/* 第二遍 -- 查找现有策略并更新 */
	/* 添加关键 - 策略的关系 ID。 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_table_id));

	/* 添加关键 - 策略的名称。 */
	ScanKeyInit(&fc_skey[1],
				Anum_pg_policy_polname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_stmt->subname));

	fc_sscan = systable_beginscan(fc_pg_policy_rel,
							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
							   fc_skey);

	fc_policy_tuple = systable_getnext(fc_sscan);

	/* 如果没有找到该策略则抱怨 */
	if (!HeapTupleIsValid(fc_policy_tuple))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("policy \"%s\" for table \"%s\" does not exist",
						fc_stmt->subname, RelationGetRelationName(fc_target_table))));

	fc_opoloid = ((Form_pg_policy) GETSTRUCT(fc_policy_tuple))->oid;

	fc_policy_tuple = heap_copytuple(fc_policy_tuple);

	namestrcpy(&((Form_pg_policy) GETSTRUCT(fc_policy_tuple))->polname,
			   fc_stmt->newname);

	CatalogTupleUpdate(fc_pg_policy_rel, &fc_policy_tuple->t_self, fc_policy_tuple);

	InvokeObjectPostAlterHook(PolicyRelationId, fc_opoloid, 0);

	ObjectAddressSet(fc_address, PolicyRelationId, fc_opoloid);

	/*
	 * 使关系的 relcache 条目失效，以便其他后端（以及这个后端！）收到 SI 消息以重新构建 relcache 条目。
	 * （理想情况下，这应该自动发生...）
	 */
	CacheInvalidateRelcache(fc_target_table);

	/* 清理。 */
	systable_endscan(fc_sscan);
	table_close(fc_pg_policy_rel, RowExclusiveLock);
	relation_close(fc_target_table, NoLock);

	return fc_address;
}

/*
 * get_relation_policy_oid - 通过名称查找策略以找到其 OID
 *
 * 如果 missing_ok 为 false，则如果未找到策略则抛出错误。如果
 * 为 true，则只返回 InvalidOid。
 */
Oid get_relation_policy_oid(Oid fc_relid, const char *fc_policy_name, bool fc_missing_ok)
{
	Relation	fc_pg_policy_rel;
	ScanKeyData fc_skey[2];
	SysScanDesc fc_sscan;
	HeapTuple	fc_policy_tuple;
	Oid			fc_policy_oid;

	fc_pg_policy_rel = table_open(PolicyRelationId, AccessShareLock);

	/* 添加关键 - 策略的关系 ID。 */
	ScanKeyInit(&fc_skey[0],
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_relid));

	/* 添加关键 - 策略的名称。 */
	ScanKeyInit(&fc_skey[1],
				Anum_pg_policy_polname,
				BTEqualStrategyNumber, F_NAMEEQ,
				CStringGetDatum(fc_policy_name));

	fc_sscan = systable_beginscan(fc_pg_policy_rel,
							   PolicyPolrelidPolnameIndexId, true, NULL, 2,
							   fc_skey);

	fc_policy_tuple = systable_getnext(fc_sscan);

	if (!HeapTupleIsValid(fc_policy_tuple))
	{
		if (!fc_missing_ok)
			ereport(ERROR,
					(errcode(ERRCODE_UNDEFINED_OBJECT),
					 errmsg("policy \"%s\" for table \"%s\" does not exist",
							fc_policy_name, get_rel_name(fc_relid))));

		fc_policy_oid = InvalidOid;
	}
	else
		fc_policy_oid = ((Form_pg_policy) GETSTRUCT(fc_policy_tuple))->oid;

	/* 清理。 */
	systable_endscan(fc_sscan);
	table_close(fc_pg_policy_rel, AccessShareLock);

	return fc_policy_oid;
}

/*
 * relation_has_policies - 确定关系是否有任何政策
 */
bool relation_has_policies(Relation fc_rel)
{
	Relation	fc_catalog;
	ScanKeyData fc_skey;
	SysScanDesc fc_sscan;
	HeapTuple	fc_policy_tuple;
	bool		fc_ret = false;

	fc_catalog = table_open(PolicyRelationId, AccessShareLock);
	ScanKeyInit(&fc_skey,
				Anum_pg_policy_polrelid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(RelationGetRelid(fc_rel)));
	fc_sscan = systable_beginscan(fc_catalog, PolicyPolrelidPolnameIndexId, true,
							   NULL, 1, &fc_skey);
	fc_policy_tuple = systable_getnext(fc_sscan);
	if (HeapTupleIsValid(fc_policy_tuple))
		fc_ret = true;

	systable_endscan(fc_sscan);
	table_close(fc_catalog, AccessShareLock);

	return fc_ret;
}
